package com.example.points.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.points.domain.UserPoints;
import com.example.points.domain.PointsDetail;
import com.example.points.domain.PointsOperationLog;
import com.example.points.mapper.UserPointsMapper;
import com.example.points.mapper.PointsDetailMapper;
import com.example.points.mapper.PointsOperationLogMapper;
import com.example.points.service.UserPointsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserPointsServiceImpl extends ServiceImpl<UserPointsMapper, UserPoints> implements UserPointsService {

    @Autowired
    private PointsDetailMapper pointsDetailMapper;

    @Autowired
    private PointsOperationLogMapper operationLogMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String POINTS_CACHE_KEY = "user:points:";
    private static final long CACHE_EXPIRE_TIME = 1; // 1 hour
    private static final int MAX_RETRY_TIMES = 3; // 最大重试次数
    
    @Override
    public UserPoints getUserPoints(Long userId) {
        String cacheKey = POINTS_CACHE_KEY + userId;
        UserPoints userPoints = (UserPoints) redisTemplate.opsForValue().get(cacheKey);
        
        if (userPoints == null) {
            userPoints = lambdaQuery().eq(UserPoints::getUserId, userId).one();
            if (userPoints != null) {
                redisTemplate.opsForValue().set(cacheKey, userPoints, CACHE_EXPIRE_TIME, TimeUnit.HOURS);
            }
        }
        
        return userPoints;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rechargePoints(Long userId, Long points, String description) {
        int retryTimes = 0;
        while (retryTimes < MAX_RETRY_TIMES) {
            try {
                UserPoints userPoints = getUserPoints(userId);
                if (userPoints == null) {
                    userPoints = new UserPoints();
                    userPoints.setUserId(userId);
                    userPoints.setTotalPoints(0L);
                    userPoints.setAvailablePoints(0L);
                    userPoints.setFrozenPoints(0L);
                    userPoints.setVersion(0); // 初始化版本号
                    save(userPoints);
                    // 重新获取，确保有版本号
                    userPoints = getUserPoints(userId);
                }

                Long beforePoints = userPoints.getAvailablePoints();
                userPoints.setTotalPoints(userPoints.getTotalPoints() + points);
                userPoints.setAvailablePoints(userPoints.getAvailablePoints() + points);
                
                boolean success = updateById(userPoints);
                if (success) {
                    // 记录积分明细
                    PointsDetail detail = new PointsDetail();
                    detail.setUserId(userId);
                    detail.setPoints(points);
                    detail.setType(1); // 充值
                    detail.setStatus(1);
                    detail.setDescription(description);
                    detail.setExpireTime(LocalDateTime.now().plusYears(1)); // 设置1年后过期
                    pointsDetailMapper.insert(detail);

                    // 记录操作日志
                    PointsOperationLog log = new PointsOperationLog();
                    log.setUserId(userId);
                    log.setOperationType(1);
                    log.setPoints(points);
                    log.setBeforePoints(beforePoints);
                    log.setAfterPoints(userPoints.getAvailablePoints());
                    log.setRemark(description);
                    operationLogMapper.insert(log);

                    // 更新缓存
                    redisTemplate.delete(POINTS_CACHE_KEY + userId);
                    return true;
                }
            } catch (Exception e) {
                retryTimes++;
                if (retryTimes >= MAX_RETRY_TIMES) {
                    throw e;
                }
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean consumePoints(Long userId, Long points, String description) {
        int retryTimes = 0;
        while (retryTimes < MAX_RETRY_TIMES) {
            try {
                UserPoints userPoints = getUserPoints(userId);
                if (userPoints == null || userPoints.getAvailablePoints() < points) {
                    return false;
                }

                Long beforePoints = userPoints.getAvailablePoints();
                userPoints.setTotalPoints(userPoints.getTotalPoints() - points);
                userPoints.setAvailablePoints(userPoints.getAvailablePoints() - points);
                
                boolean success = updateById(userPoints);
                if (success) {
                    // 记录积分明细
                    PointsDetail detail = new PointsDetail();
                    detail.setUserId(userId);
                    detail.setPoints(-points);
                    detail.setType(2); // 消费
                    detail.setStatus(1);
                    detail.setDescription(description);
                    pointsDetailMapper.insert(detail);

                    // 记录操作日志
                    PointsOperationLog log = new PointsOperationLog();
                    log.setUserId(userId);
                    log.setOperationType(2);
                    log.setPoints(points);
                    log.setBeforePoints(beforePoints);
                    log.setAfterPoints(userPoints.getAvailablePoints());
                    log.setRemark(description);
                    operationLogMapper.insert(log);

                    // 更新缓存
                    redisTemplate.delete(POINTS_CACHE_KEY + userId);
                    return true;
                }
            } catch (Exception e) {
                retryTimes++;
                if (retryTimes >= MAX_RETRY_TIMES) {
                    throw e;
                }
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean freezePoints(Long userId, Long points) {
        int retryTimes = 0;
        while (retryTimes < MAX_RETRY_TIMES) {
            try {
                UserPoints userPoints = getUserPoints(userId);
                if (userPoints == null || userPoints.getAvailablePoints() < points) {
                    return false;
                }

                Long beforePoints = userPoints.getAvailablePoints();
                userPoints.setAvailablePoints(userPoints.getAvailablePoints() - points);
                userPoints.setFrozenPoints(userPoints.getFrozenPoints() + points);
                
                boolean success = updateById(userPoints);
                if (success) {
                    // 记录操作日志
                    PointsOperationLog log = new PointsOperationLog();
                    log.setUserId(userId);
                    log.setOperationType(4);
                    log.setPoints(points);
                    log.setBeforePoints(beforePoints);
                    log.setAfterPoints(userPoints.getAvailablePoints());
                    log.setRemark("积分冻结");
                    operationLogMapper.insert(log);

                    // 更新缓存
                    redisTemplate.delete(POINTS_CACHE_KEY + userId);
                    return true;
                }
            } catch (Exception e) {
                retryTimes++;
                if (retryTimes >= MAX_RETRY_TIMES) {
                    throw e;
                }
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean unfreezePoints(Long userId, Long points) {
        int retryTimes = 0;
        while (retryTimes < MAX_RETRY_TIMES) {
            try {
                UserPoints userPoints = getUserPoints(userId);
                if (userPoints == null || userPoints.getFrozenPoints() < points) {
                    return false;
                }

                Long beforePoints = userPoints.getAvailablePoints();
                userPoints.setAvailablePoints(userPoints.getAvailablePoints() + points);
                userPoints.setFrozenPoints(userPoints.getFrozenPoints() - points);
                
                boolean success = updateById(userPoints);
                if (success) {
                    // 记录操作日志
                    PointsOperationLog log = new PointsOperationLog();
                    log.setUserId(userId);
                    log.setOperationType(5);
                    log.setPoints(points);
                    log.setBeforePoints(beforePoints);
                    log.setAfterPoints(userPoints.getAvailablePoints());
                    log.setRemark("积分解冻");
                    operationLogMapper.insert(log);

                    // 更新缓存
                    redisTemplate.delete(POINTS_CACHE_KEY + userId);
                    return true;
                }
            } catch (Exception e) {
                retryTimes++;
                if (retryTimes >= MAX_RETRY_TIMES) {
                    throw e;
                }
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchRechargePoints(List<Long> userIds, Long points, String description) {
        for (Long userId : userIds) {
            rechargePoints(userId, points, description);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchConsumePoints(List<Long> userIds, Long points, String description) {
        for (Long userId : userIds) {
            consumePoints(userId, points, description);
        }
        return true;
    }

    @Override
    public void handleExpiredPoints() {
        LocalDateTime now = LocalDateTime.now();
        
        LambdaQueryWrapper<PointsDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PointsDetail::getStatus, 1)
                   .le(PointsDetail::getExpireTime, now);
        
        List<PointsDetail> expiredDetails = pointsDetailMapper.selectList(queryWrapper);

        for (PointsDetail detail : expiredDetails) {
            UserPoints userPoints = getUserPoints(detail.getUserId());
            if (userPoints != null && userPoints.getAvailablePoints() >= detail.getPoints()) {
                // 扣减过期积分
                consumePoints(detail.getUserId(), detail.getPoints(), "积分过期");
                // 更新积分明细状态
                detail.setStatus(0);
                pointsDetailMapper.updateById(detail);
                
                // 发送过期提醒到Redis队列
                String message = String.format("用户%d的%d积分已过期", detail.getUserId(), detail.getPoints());
                redisTemplate.opsForList().rightPush("points:expire:notifications", message);
            }
        }
    }
} 